/**************************************************************************** 
* 
* Copyright (c) 2023  C*Core -   All Rights Reserved  
* 
* THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, 
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* 
* PROJECT     : CCFC2011BC           
* DESCRIPTION : CCFC2011BC can low level drivers code 
* HISTORY     : Initial version.
* @file     can_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/

#include "can_lld.h"

static uint32_t g_tx_mb_idx_bitmapl[CAN_MOD_BUTT];
static uint32_t g_tx_mb_idx_bitmaph[CAN_MOD_BUTT];
static uint32_t g_rx_mb_idx_offset[CAN_MOD_BUTT];
 CANIntrCallback g_intr_callback[CAN_MOD_BUTT];
static CanTxMbState g_tx_mb_state[CAN_MOD_BUTT][CAN_TOTAL_MAIL_BOX_NUM];

/*******************************************************************************
 * @brief      Get CAN handle though ID
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
CANDriver* Can_LLD_Get_Driver(CANModId can_id)
{
    static CANDriver g_can_driver[CAN_MOD_BUTT];
    return &g_can_driver[can_id];
}
static volatile CANReg* Can_LLD_Get_Reg(CANModId can_id)
{
    static volatile CANReg * const reg[CAN_MOD_BUTT] =
    {
        (volatile CANReg*)&CAN_0,
        (volatile CANReg*)&CAN_1,
        (volatile CANReg*)&CAN_2,
        (volatile CANReg*)&CAN_3,
        (volatile CANReg*)&CAN_4,
        (volatile CANReg*)&CAN_5,
    };
    return reg[can_id];
}

/*******************************************************************************
 * @brief      Can_LLD_Cfg_Check
 * @param[in]  const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 uint32_t Can_LLD_Cfg_Check(const CANConfig *config)
{
    uint32_t fifo_use_mb_num = Can_LLD_Get_FIFO_Use_MB_Num(config);

    if (config->can_id >= CAN_MOD_BUTT)
    {
        return CAN_ERROR_INVALID_PARA;
    }

    if (config->phy_para.bit_rate >= CAN_BIT_RATE_BUTT)
    {
        return CAN_ERROR_INVALID_PARA;
    }

    if ((fifo_use_mb_num + config->buf_cfg.rx_mb_num) >= CAN_TOTAL_MAIL_BOX_NUM)
    {
        return CAN_ERROR_MAIL_BOX_ARRANGE;
    }

    return CAN_OK;
}

/*******************************************************************************
 * @brief      Can_LLD_Init_Port
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Init_Port(CANModId can_id)
{
    static const struct
    {
        uint32_t tx_id;
        uint32_t tx_val;
        uint32_t rx_id;
        uint32_t rx_val;
    } pcr_cfg[CAN_MOD_BUTT] =
    {
        /* CAN_MOD_ID_0: port B0 as CAN0TX, open drain ; port B1 as CAN0RX */
        {16, 0x0604, 17, 0x0100},
        /* CAN_MOD_ID_1: port C10 as CAN1TX, open drain ; port C11 as CAN1RX */
        {42, 0x0604, 43, 0x0100},
        /* CAN_MOD_ID_2: port E8  as CAN2TX, open drain ; port E9  as CAN2RX */
        {72, 0x0604, 73, 0x0100},
        /* CAN_MOD_ID_3: port C5  as CAN3TX, open drain ; port C4  as CAN3RX */
        {37, 0x0A04, 36, 0x0100},
        /* CAN_MOD_ID_4: port C2  as CAN4TX, open drain ; port C3  as CAN4RX */
        {34, 0x0A04, 35, 0x0100},
        /* CAN_MOD_ID_5: port E1  as CAN5TX, open drain ; port E0  as CAN5RX */
        {65, 0x0A04, 64, 0x0100},
    };

    SIU.PCR[pcr_cfg[can_id].tx_id].R = (uint16_t)pcr_cfg[can_id].tx_val;
    SIU.PCR[pcr_cfg[can_id].rx_id].R = (uint16_t)pcr_cfg[can_id].rx_val;

    if (can_id == CAN_MOD_ID_1)
    {
        SIU.PSMI[0].B.PADSEL = 0x01; /* select PC11 as FLEXCAN1 RX*/
    }
}

/*******************************************************************************
 * @brief      Can_LLD_Config_Clock
 * @param[in]  CANModId can_id, uint8_t is_enable
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Config_Clock(CANModId can_id, uint8_t is_enable)
{
    static const struct
    {
        uint32_t reg_idx;
        uint32_t reg_val;
    } enable_cfg[CAN_MOD_BUTT] =
    {
        {16, CAN_FLEXCAN0_START_PCTL}, // CAN_MOD_ID_0,
        {17, CAN_FLEXCAN1_START_PCTL}, // CAN_MOD_ID_1,
        {18, CAN_FLEXCAN2_START_PCTL}, // CAN_MOD_ID_2,
        {19, CAN_FLEXCAN3_START_PCTL}, // CAN_MOD_ID_3,
        {20, CAN_FLEXCAN4_START_PCTL}, // CAN_MOD_ID_4,
        {21, CAN_FLEXCAN5_START_PCTL}, // CAN_MOD_ID_5,
    }, disable_cfg[CAN_MOD_BUTT] =
    {
        {16, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_0,
        {17, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_1,
        {18, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_2,
        {19, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_3,
        {20, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_4,
        {21, CAN_FLEXCAN0_STOP_PCTL}, // CAN_MOD_ID_5,
    };

    if (is_enable != 0U)
    {
        SetPeripheralClockMode(enable_cfg[can_id].reg_idx,
            enable_cfg[can_id].reg_val);
    }
    else
    {
        SetPeripheralClockMode(disable_cfg[can_id].reg_idx,
            disable_cfg[can_id].reg_val);
    }
}

/*******************************************************************************
 * @brief      Can_LLD_Set_Rate
 * @param[in]  volatile CANReg *reg, const CANConfig *config)
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Can_LLD_Set_Rate(volatile CANReg *reg, const CANConfig *config)
{
    uint32_t bit_rate = (uint32_t)config->phy_para.bit_rate;
    static const CANTimeSeg time_seg[] =
    {
        {1, 2, 1, 3, 7},//CAN_MBPS_1
        {2, 2, 2, 3, 7},//CAN_KBPS_800
        {1, 2, 1, 3, 7},//CAN_KBPS_500 15
        {1, 2, 1, 3, 31},//CAN_KBPS_250
        {1, 2, 1, 3, 63},//CAN_KBPS_125
        {2, 2, 2, 3, 63},//CAN_KBPS_100
    };
    //btr = canclk/(PRESDIV+1)/(1+(PROPSEG+PSEG1+2)+(PSEG2+1))
    //Sampling points    = (1+(PROPSEG+PSEG1+2)+(PSEG2+1)) / (1+(PROPSEG+PSEG1+2)+(PSEG2+1)+RJW)
    reg->CR.B.PROPSEG = time_seg[bit_rate].prop_seg;
    reg->CR.B.PSEG1 = time_seg[bit_rate].time_seg1;
    reg->CR.B.PSEG2 = time_seg[bit_rate].time_seg2;
    reg->CR.B.RJW = time_seg[bit_rate].rjw;
    reg->CR.B.PRESDIV = time_seg[bit_rate].pres_div;
    reg->CR.B.TSYN = ENABLE;
}

/*******************************************************************************
 * @brief      Can_LLD_Alloc_Tx_MB
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 uint32_t Can_LLD_Alloc_Tx_MB(CANModId can_id)
{
    uint32_t offset, mb_idx;

    if (g_tx_mb_idx_bitmapl[can_id] != 0U)
    {
        offset = __builtin_ctz(g_tx_mb_idx_bitmapl[can_id]);
        BIT_CLR(g_tx_mb_idx_bitmapl[can_id], offset); /* PRQA S 3335, 3473 # simple macro function */
        mb_idx = offset;
    }
    else if (g_tx_mb_idx_bitmaph[can_id] != 0U)
    {
        offset = __builtin_ctz(g_tx_mb_idx_bitmaph[can_id]);
        BIT_CLR(g_tx_mb_idx_bitmaph[can_id], offset); /* PRQA S 3335, 3473 # simple macro function */
        mb_idx = offset + CAN_WORD_INDICATE_MAIL_BOX_NUM;
    }
    else
    {
        mb_idx = CAN_TOTAL_MAIL_BOX_NUM; /* No TX MB is available */
    }
    return mb_idx;
}

/*******************************************************************************
 * @brief      Can_LLD_Release_Tx_MB
 * @param[in]  CANModId can_id, uint32_t mb_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Can_LLD_Release_Tx_MB(CANModId can_id, uint32_t mb_idx)
{
    uint32_t offset;

    if (mb_idx < CAN_WORD_INDICATE_MAIL_BOX_NUM)
    {
        offset = mb_idx;
        BIT_SET(g_tx_mb_idx_bitmapl[can_id], offset); /* PRQA S 3335, 3473 # simple macro function */
    }
    else
    {
        offset = mb_idx - CAN_WORD_INDICATE_MAIL_BOX_NUM;
        BIT_SET(g_tx_mb_idx_bitmaph[can_id], offset); /* PRQA S 3335, 3473 # simple macro function */
    }
}

/*******************************************************************************
 * @brief      Can_LLD_Init_Tx_MB
 * @param[in]  volatile CANReg *reg, const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Init_Tx_MB(volatile CANReg *reg, const CANConfig *config)
{
    uint32_t idx;
    uint32_t fifo_use_mb_num = Can_LLD_Get_FIFO_Use_MB_Num(config);
    uint64_t tx_mb_idx_bitmap = 0;

    reg->MCR.B.AEN = ENABLE;
    reg->MCR.B.LPRIO_EN = ENABLE;

    /* Configure the TX MB status */
     for (idx = fifo_use_mb_num + config->buf_cfg.rx_mb_num;
         idx < CAN_TOTAL_MAIL_BOX_NUM; idx++)
     {
         reg->BUF[idx].CS.B.CODE = CAN_TX_MB_STATE_INACTIVE;
         tx_mb_idx_bitmap = tx_mb_idx_bitmap | (1ULL << idx);
     }

    /* Record TX MB parameters */
    g_tx_mb_idx_bitmapl[config->can_id] = (uint32_t)tx_mb_idx_bitmap;
    g_tx_mb_idx_bitmaph[config->can_id] = (uint32_t)(tx_mb_idx_bitmap >> 32ULL);
}

/*******************************************************************************
 * @brief      Can_LLD_Get_Rx_MB_Idx_Offset
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint32_t Can_LLD_Get_Rx_MB_Idx_Offset(CANModId can_id)
{
    return g_rx_mb_idx_offset[can_id];
}

/*******************************************************************************
 * @brief      Can_LLD_Init_Rx_MB
 * @param[in]  volatile CANReg *reg, const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Init_Rx_MB(volatile CANReg *reg, const CANConfig *config)
{
    uint32_t idx;
    uint32_t fifo_use_mb_num = Can_LLD_Get_FIFO_Use_MB_Num(config);

    reg->MCR.B.BCC = config->func_switch.bcc_en; /* Individual Rx masking and queue feature */

    for (idx = 0; idx < config->buf_cfg.rx_mb_num; idx++)
    {
        reg->BUF[fifo_use_mb_num + idx].CS.B.CODE = CAN_RX_MB_STATE_INACTIVE;
        reg->BUF[fifo_use_mb_num + idx].CS.B.RTR = config->mb[idx].is_remote;
        reg->BUF[fifo_use_mb_num + idx].CS.B.IDE = config->mb[idx].is_extern;
        reg->BUF[fifo_use_mb_num + idx].ID.R = 0;
        if (config->mb[idx].is_extern != 0U)
        {
            reg->BUF[fifo_use_mb_num + idx].ID.R |= (config->mb[idx].id & 0x1FFFFFFFU); /* extern ID */
            reg->RXIMR[fifo_use_mb_num + idx].B.MI = config->mb[idx].mask;
        }
        else
        {
            reg->BUF[fifo_use_mb_num + idx].ID.B.STD_ID = config->mb[idx].id;
            reg->RXIMR[fifo_use_mb_num + idx].B.MI = config->mb[idx].mask << 18U;
        }
        reg->BUF[fifo_use_mb_num + idx].CS.B.CODE = CAN_RX_MB_STATE_WAIT_MSG;
    }

    /* Record TX MB parameters */
    g_rx_mb_idx_offset[config->can_id] = fifo_use_mb_num;
}

/*******************************************************************************
 * @brief      Can_LLD_Init_Rx_FIFO
 * @param[in]  volatile CANReg *reg, const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Init_Rx_FIFO(volatile CANReg *reg, const CANConfig *config)
{
    uint32_t idx;
    CANFifo *fifo = (CANFifo *)&reg->BUF[0]; /* PRQA S 0310 # legal pointer type conversion */

    if (config->func_switch.fifo_en != TRUE)
    {
        return;
    }

    for (idx = 0; idx < CAN_FIFO_USE_MAIL_BOX_NUM; idx++)
    {
        reg->BUF[idx].CS.B.CODE = CAN_RX_MB_STATE_INACTIVE;
    }

    for (idx = 0; idx < CAN_FIFO_FILTER_TABLE_NUM; idx++)
    {
        fifo->IDTABLE[idx].R = 0;
        fifo->IDTABLE[idx].B.REM = config->fifo[idx].is_remote;
        fifo->IDTABLE[idx].B.EXT = config->fifo[idx].is_extern;
        if (config->fifo[idx].is_extern != 0U)
        {
            fifo->IDTABLE[idx].R |= ((config->fifo[idx].id & 0x1FFFFFFFU) << 1); /* extern ID */
            reg->RXIMR[idx].B.MI = config->fifo[idx].mask;
        }
        else
        {
            fifo->IDTABLE[idx].B.STD_ID = config->fifo[idx].id;
            reg->RXIMR[idx].B.MI = config->fifo[idx].mask << 18U;
        }
    }

    reg->MCR.B.FEN = ENABLE;
    reg->MCR.B.IDAM = 0; /* CAN_RX_FIFO_FORMAT_A */
}

/*******************************************************************************
 * @brief      Can_LLD_Bind_Msg_Id_To_MB
 * @param[in]  CANModId can_id, uint32_t mb_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 uint32_t Can_LLD_Bind_Msg_Id_To_MB(CANModId can_id, uint32_t mb_idx)
{
    static CanTxMbMsgId msg_id;

    msg_id.attr.msg_cnt++;
    msg_id.attr.is_valid = TRUE;
    g_tx_mb_state[can_id][mb_idx].cur_msg = msg_id.id;

    return g_tx_mb_state[can_id][mb_idx].cur_msg;
}

/*******************************************************************************
 * @brief      Can_LLD_Get_Msg_Id_From_MB
 * @param[in]  CANModId can_id, uint32_t mb_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 uint32_t Can_LLD_Get_Msg_Id_From_MB(CANModId can_id, uint32_t mb_idx)
{
    return g_tx_mb_state[can_id][mb_idx].cur_msg;
}

/*******************************************************************************
 * @brief      Can_LLD_Clear_Msg_Id_In_MB
 * @param[in]  CANModId can_id, uint32_t mb_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Can_LLD_Clear_Msg_Id_In_MB(CANModId can_id, uint32_t mb_idx)
{
    g_tx_mb_state[can_id][mb_idx].cur_msg = 0;
}

/*******************************************************************************
 * @brief      Can_LLD_Select_Run_Mode
 * @param[in]  CANDriver *cand, volatile CANReg *reg, const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Select_Run_Mode(CANDriver *cand, volatile CANReg *reg, const CANConfig *config)
{
    if (config->func_switch.loop_back_en == ENABLE)
    {
        /* loop back mode */
        reg->CR.B.LPB = config->func_switch.loop_back_en;
        cand->state.cur_mode = CAN_CUR_MODE_LOOP_BACK;
    }
    else if (config->func_switch.listen_only_en == ENABLE)
    {
        /* listen only mode */
        reg->CR.B.LOM = config->func_switch.listen_only_en;
        cand->state.cur_mode = CAN_CUR_MODE_LISTEN_ONLY;
    }
    else
    {
        cand->state.cur_mode = CAN_CUR_MODE_NORMAL;
    }
}

/*******************************************************************************
 * @brief      Can_LLD_Get_Buf_In_MB
 * @param[in]  volatile CANReg *reg, uint32_t mb_idx, CANRxMsg *rx_msg
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Can_LLD_Get_Buf_In_MB(volatile CANReg *reg, uint32_t mb_idx, CANRxMsg *rx_msg)
{
    volatile uint32_t cs_code, cur_time;

    /* lock mail box */
    cs_code = reg->BUF[mb_idx].CS.R;

    /* read mail box */
    rx_msg->data.word[0] = reg->BUF[mb_idx].DATA.R[0];
    rx_msg->data.word[1] = reg->BUF[mb_idx].DATA.R[1];
    rx_msg->is_extern = reg->BUF[mb_idx].CS.B.IDE;
    rx_msg->is_remote = reg->BUF[mb_idx].CS.B.RTR;
    if (rx_msg->is_extern != 0U)
    {
        rx_msg->id = reg->BUF[mb_idx].ID.R & 0x1FFFFFFFU; /* extern ID */
    }
    else
    {
        rx_msg->id = reg->BUF[mb_idx].ID.B.STD_ID;
    }
    rx_msg->data_len = reg->BUF[mb_idx].CS.B.LENGTH;
    rx_msg->timestamp = reg->BUF[mb_idx].CS.B.TIMESTAMP;

    /* unlock mail box */
    cur_time = reg->TIMER.R;

    /* release mail box */
    reg->BUF[mb_idx].CS.B.CODE = CAN_RX_MB_STATE_WAIT_MSG;
}

/*******************************************************************************
 * @brief      Can_LLD_Get_Buf_In_FIFO
 * @param[in]  volatile const CANReg *reg, CANRxMsg *rx_msg
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Get_Buf_In_FIFO(volatile const CANReg *reg, CANRxMsg *rx_msg)
{
    rx_msg->data.word[0] = reg->BUF[0].DATA.R[0];
    rx_msg->data.word[1] = reg->BUF[0].DATA.R[1];
    rx_msg->is_extern = reg->BUF[0].CS.B.IDE;
    rx_msg->is_remote = reg->BUF[0].CS.B.RTR;
    if (rx_msg->is_extern != 0U)
    {
        rx_msg->id = reg->BUF[0].ID.R & 0x1FFFFFFFU; /* extern ID */
    }
    else
    {
        rx_msg->id = reg->BUF[0].ID.B.STD_ID;
    }
    rx_msg->data_len = reg->BUF[0].CS.B.LENGTH;
    rx_msg->timestamp = reg->BUF[0].CS.B.TIMESTAMP;
}

/*******************************************************************************
 * @brief      Can_LLD_Init_Intr
 * @param[in]  volatile CANReg *reg, const CANConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Init_Intr(volatile CANReg *reg, const CANConfig *config)
{
    uint32_t idx, offset;
    uint32_t imrl = 0xFFFFFFFFU;
    uint32_t imrh = 0xFFFFFFFFU;
    uint32_t fifo_use_mb_num = Can_LLD_Get_FIFO_Use_MB_Num(config);

    /* hook callback */
    cc_memcpy((uint8_t *)&g_intr_callback[config->can_id],
        (const uint8_t *)&config->call_back, sizeof(CANIntrCallback));
    switch (config->can_id)
    {
        case CAN_MOD_ID_0 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_0);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_0); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_0),
                    (uint16_t)idx, CAN_FLEXCAN0_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_0),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_0), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CAN_MOD_ID_1 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_1);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_1); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_1),
                    (uint16_t)idx, CAN_FLEXCAN1_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_1),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_1), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CAN_MOD_ID_2 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_2);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_2); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_2),
                    (uint16_t)idx, CAN_FLEXCAN2_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_2),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_2), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CAN_MOD_ID_3 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_3);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_3); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_3),
                    (uint16_t)idx, CAN_FLEXCAN3_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_3),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_3), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CAN_MOD_ID_4 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_4);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_4); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_4),
                    (uint16_t)idx, CAN_FLEXCAN4_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_4),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_4), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CAN_MOD_ID_5 :
        {
            for (idx = CAN_LLD_INTR_MSG_00_03_NUM(CAN_MOD_ID_5);
                idx <= CAN_LLD_INTR_MSG_32_63_NUM(CAN_MOD_ID_5); idx++)
            {
                INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_MSG_HANDLER_NAME(CAN_MOD_ID_5),
                    (uint16_t)idx, CAN_FLEXCAN5_IRQ_PRIORITY);
            }
            INTC_InstallINTCInterruptHandler(CAN_LLD_INTR_ERR_HANDLER_NAME(CAN_MOD_ID_5),
                CAN_LLD_INTR_ESR_ERROR_NUM(CAN_MOD_ID_5), CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        default :
        {
            break;
        }
    }

    /* enable interrupt */
    if (config->call_back.rx_fifo_msg_received == NULL)
    {
        for (idx = 0; idx < fifo_use_mb_num; idx++)
        {
            offset = idx;
            BIT_CLR(imrl, offset); /* PRQA S 3335, 3473 # simple macro function */
        }
    }
    if (config->call_back.rx_mb_msg_received == NULL)
    {
        for (idx = fifo_use_mb_num;
            idx < (fifo_use_mb_num + config->buf_cfg.rx_mb_num); idx++)
        {
            if (idx < CAN_WORD_INDICATE_MAIL_BOX_NUM)
            {
                offset = idx;
                BIT_CLR(imrl, offset); /* PRQA S 3335, 3473 # simple macro function */
            }
            else
            {
                offset = idx - CAN_WORD_INDICATE_MAIL_BOX_NUM;
                BIT_CLR(imrh, offset); /* PRQA S 3335, 3473 # simple macro function */
            }
        }
    }

    /* enable interrupt */
    reg->IMRL.R = imrl;
    reg->IMRH.R = imrh;
    reg->MCR.B.WRNEN = ENABLE; /* Warning */
    reg->CR.B.TWRNMSK = ENABLE; /* Tx Warning */
    reg->CR.B.RWRNMSK = ENABLE; /* Rx Warning */
    reg->CR.B.BOFFMSK = ENABLE; /* Bus Off */
    reg->CR.B.ERRMSK = ENABLE; /* Error */
}

/*******************************************************************************
 * @brief      Can_LLD_Intr_Msg_Callback
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Intr_Msg_Callback(CANModId can_id)
{
    CANDriver *cand = NULL;
    volatile CANReg *reg = NULL;
    uint32_t idx, mb_idx;
    uint32_t bit_offset, mb_offset;
    CANRxMsg rx_msg;
    static struct
    {
        vuint32_t *iflag_addr;
        vuint32_t *imask_addr;
    } iflag[2]; /* indicate L and H */
    volatile uint32_t intr_flag_after_mask;

    /* fetch register */
    reg = Can_LLD_Get_Reg(can_id);

    iflag[0].iflag_addr = &reg->IFRL.R;
    iflag[0].imask_addr = &reg->IMRL.R;
    iflag[1].iflag_addr = &reg->IFRH.R;
    iflag[1].imask_addr = &reg->IMRH.R;

    /* message interrupt */
    for (idx = 0, mb_offset = 0; /* PRQA S 2462, 2463, 3418 */
        idx < (sizeof(iflag) / sizeof(iflag[0])); idx++, mb_offset += CAN_WORD_INDICATE_MAIL_BOX_NUM) /* PRQA S 3387, 3418 */
    {
        while ((intr_flag_after_mask = (*iflag[idx].iflag_addr) & (*iflag[idx].imask_addr)) != 0U) /* PRQA S 0404, 3326 */
        {
            bit_offset = __builtin_ctz(intr_flag_after_mask);
            mb_idx = bit_offset + mb_offset;
            cand = Can_LLD_Get_Driver(can_id);
            switch (reg->BUF[mb_idx].CS.B.CODE)
            {
                case CAN_TX_MB_STATE_AFTER_TRANS_DATA_MSG :
                {
                    if (g_intr_callback[can_id].tx_mb_msg_transferred != NULL)
                    {
                        g_intr_callback[can_id].tx_mb_msg_transferred(cand, mb_idx);
                    }
                    /* clear msg_id */
                    Can_LLD_Clear_Msg_Id_In_MB(can_id, mb_idx);
                    /* release MB */
                    Can_LLD_Release_Tx_MB(can_id, mb_idx);
                    break;
                }
                case CAN_TX_MB_STATE_ABORT_TRANS_CUR_MSG :
                {
                    if (g_intr_callback[can_id].tx_mb_msg_aborted != NULL)
                    {
                        g_intr_callback[can_id].tx_mb_msg_aborted(cand, mb_idx);
                    }
                    break;
                }
                case CAN_RX_MB_STATE_OVERWRITTEN :
                case CAN_RX_MB_STATE_RECVED_MSG :
                {
                    if (g_intr_callback[can_id].rx_mb_msg_received != NULL)
                    {
                        cc_memset((uint8_t *)&rx_msg, 0, sizeof(rx_msg));
                        Can_LLD_Get_Buf_In_MB(reg, mb_idx, &rx_msg);
                        g_intr_callback[can_id].rx_mb_msg_received(cand, mb_idx, &rx_msg);
                    }
                    break;
                }
                case CAN_RX_MB_STATE_INACTIVE :
                { /* RX FIFO */
                    if (reg->IFRL.B.BUF05I != 0U)
                    { /* BUF5I indicate Frames available in FIFO */
                        if (g_intr_callback[can_id].rx_fifo_msg_received != NULL)
                        {
                            cc_memset((uint8_t *)&rx_msg, 0, sizeof(rx_msg));
                            Can_LLD_Get_Buf_In_FIFO(reg, &rx_msg);
                            g_intr_callback[can_id].rx_fifo_msg_received(cand, &rx_msg);
                        }
                    }
                    else if (reg->IFRL.B.BUF06I != NULL)
                    { /* BUF6I indicate Frames almost full in FIFO */
                        if (g_intr_callback[can_id].rx_fifo_msg_almost_full != NULL)
                        {
                            g_intr_callback[can_id].rx_fifo_msg_almost_full(cand);
                        }
                    }
                    else if  (reg->IFRL.B.BUF07I != NULL)
                    { /* BUF6I indicate Frames truely full in FIFO */
                        if (g_intr_callback[can_id].rx_fifo_msg_truely_full != NULL)
                        {
                            g_intr_callback[can_id].rx_fifo_msg_truely_full(cand);
                        }
                    }
                    else
                    {
                        ;
                    }
                    break;
                }
                default :
                {
                    break;
                }
            }
            /* clears the interrupt flag bit */ /* write-clear  */
            *iflag[idx].iflag_addr = 1UL <<bit_offset;
        }
    }
}

/*******************************************************************************
 * @brief      Can_LLD_Intr_Err_Callback
 * @param[in]  CANModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
 void Can_LLD_Intr_Err_Callback(CANModId can_id)
{
    CANDriver *cand = Can_LLD_Get_Driver(can_id);
    volatile CANReg *reg = Can_LLD_Get_Reg(can_id);
    if  (reg->ESR.B.ERRINT != 0U)
    {
        cand->state.cur_error.error_code.BIT1ERR = reg->ESR.B.BIT1ERR;
        cand->state.cur_error.error_code.BIT0ERR = reg->ESR.B.BIT0ERR;
        cand->state.cur_error.error_code.ACKERR = reg->ESR.B.ACKERR;
        cand->state.cur_error.error_code.CRCERR = reg->ESR.B.CRCERR;
        cand->state.cur_error.error_code.FRMERR = reg->ESR.B.FRMERR;
        cand->state.cur_error.error_code.STFERR = reg->ESR.B.STFERR;
        cand->state.cur_error.error_code.TXWRN = reg->ESR.B.TXWRN;
        cand->state.cur_error.error_code.RXWRN = reg->ESR.B.RXWRN;
        cand->state.cur_error.error_code.FLTCONF = reg->ESR.B.FLTCONF;
        if (g_intr_callback[can_id].error != NULL)
        {
            g_intr_callback[can_id].error(cand);
        }
        reg->ESR.R = 0x1UL << 1; /* write-clear */ /* ERRINT indicate BIT1 */
    }
    if  (reg->ESR.B.BOFFINT != 0U)
    {
        if (g_intr_callback[can_id].busoff != NULL)
        {
            g_intr_callback[can_id].busoff(cand);
        }
        reg->ESR.R = 0x1UL << 2; /* write-clear */ /* BOFFINT indicate BIT2 */
    }

    if  (reg->ESR.B.RWRNINT != 0U)
    {
        cand->state.cur_error.rx_error_cnt = reg->ECR.B.RXECNT;
        if (g_intr_callback[can_id].rx_warning != NULL)
        {
            g_intr_callback[can_id].rx_warning(cand);
        }
        reg->ESR.R = 0x1UL << 16; /* write-clear */ /* RWRNINT indicate BIT16 */
    }
    if (reg->ESR.B.TWRNINT != 0U)
    {
        cand->state.cur_error.tx_error_cnt = reg->ECR.B.TXECNT;
        if (g_intr_callback[can_id].tx_warning != NULL)
        {
            g_intr_callback[can_id].tx_warning(cand);
        }
        reg->ESR.R = 0x1UL << 17; /* write-clear */ /* TWRNINT indicate BIT17 */
    }
}

/* send message */
/*******************************************************************************
 * @brief      Lauch an CAN frame transmission requests.
 * @param[in]  CANDriver *cand
 *             Lauch an CAN frame transmission requests.
 * @param[out] uint32_t *msg_id
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_Send_Msg(const CANDriver *cand, const CANTxMsg *tx_msg, uint32_t *msg_id)
{
    volatile CANReg *reg = NULL;
    CANModId can_id = cand->id;
    uint32_t mb_idx;

    /* alloc MB */
    mb_idx = Can_LLD_Alloc_Tx_MB(can_id);
    if (mb_idx >= CAN_TOTAL_MAIL_BOX_NUM)
    {
        return CAN_ERROR_MAIL_BOX_ALLOC;
    }

    /* fetch register */
    reg = Can_LLD_Get_Reg(can_id);

    /* check mailbox */
    if (reg->BUF[mb_idx].CS.B.CODE != CAN_TX_MB_STATE_INACTIVE)
    {
        /* The mailbox is not released here to avoid being assigned again */
        return CAN_ERROR_MAIL_BOX_STATE;
    }

    /* Fill in an email */
    if (tx_msg->is_extern != 0U)
    {
        reg->BUF[mb_idx].ID.R = 0;
        reg->BUF[mb_idx].ID.R |= (tx_msg->id & 0x1FFFFFFFU); /* extern ID */
    }
    else
    {
        reg->BUF[mb_idx].ID.B.STD_ID = tx_msg->id;
    }
    reg->BUF[mb_idx].ID.B.PRIO = tx_msg->local_prior;
    reg->BUF[mb_idx].DATA.W[0] = tx_msg->data.word[0];
    reg->BUF[mb_idx].DATA.W[1] = tx_msg->data.word[1];
    reg->BUF[mb_idx].CS.B.IDE = tx_msg->is_extern;
    reg->BUF[mb_idx].CS.B.RTR = tx_msg->is_remote;
    reg->BUF[mb_idx].CS.B.LENGTH = tx_msg->data_len;

    /* send mail */
    reg->BUF[mb_idx].CS.B.CODE = CAN_TX_MB_STATE_START_TRANS_DATA_MSG;

    /* Feedback message ID */
    *msg_id = Can_LLD_Bind_Msg_Id_To_MB(can_id, mb_idx);

    return CAN_OK;
}

/*******************************************************************************
 * @brief      Check if a transmission is finished.
 * @param[in]  CANDriver *cand
 *             uint32_t msg_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint8_t Can_LLD_Is_Msg_Transferred(const CANDriver *cand, uint32_t msg_id)
{
    uint32_t mb_idx;
    CANModId can_id = cand->id;

    for (mb_idx = 0; mb_idx < CAN_TOTAL_MAIL_BOX_NUM; mb_idx++)
    {
        if (msg_id == Can_LLD_Get_Msg_Id_From_MB(can_id, mb_idx))
        {
            break;
        }
    }
    if (mb_idx == CAN_TOTAL_MAIL_BOX_NUM)
    {
        return TRUE;
    }

    return FALSE;
}

/*******************************************************************************
 * @brief      Abort an CAN frame transmission requests.
 * @param[in]  CANDriver *cand
 *             uint32_t msg_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_Abort_Msg_Transfer(const CANDriver *cand, uint32_t msg_id)
{
    uint32_t mb_idx;
    CANModId can_id = cand->id;
    volatile CANReg *reg = NULL;
    volatile uint32_t cs_code;

    for (mb_idx = 0; mb_idx < CAN_TOTAL_MAIL_BOX_NUM; mb_idx++)
    {
        if (msg_id == Can_LLD_Get_Msg_Id_From_MB(can_id, mb_idx))
        {
            break;
        }
    }
    if (mb_idx == CAN_TOTAL_MAIL_BOX_NUM)
    {
        return CAN_ERROR_MAIL_BOX_ABORT;
    }

    /* fetch register */
    reg = Can_LLD_Get_Reg(can_id);

    /* attempt termination */
    reg->BUF[mb_idx].CS.B.CODE = CAN_TX_MB_STATE_ABORT_TRANS_CUR_MSG;

    /* wait result */
    do
    {
        cs_code = reg->BUF[mb_idx].CS.B.CODE;
    } while ((cs_code != CAN_TX_MB_STATE_ABORT_TRANS_CUR_MSG) &&
        (cs_code != CAN_TX_MB_STATE_AFTER_TRANS_DATA_MSG)); /* PRQA S 3415 # no side effect */

    /* judgment result */
    if (cs_code != CAN_TX_MB_STATE_ABORT_TRANS_CUR_MSG)
    {
        return CAN_ERROR_MAIL_BOX_ABORT;
    }

    /* Change the MB state to INACTIVE */
    reg->BUF[mb_idx].CS.B.CODE = CAN_TX_MB_STATE_INACTIVE;

    /* clear the msg id corresponding to MB */
    Can_LLD_Clear_Msg_Id_In_MB(can_id, mb_idx);

    /* release MB */
    Can_LLD_Release_Tx_MB(can_id, mb_idx);

    return CAN_OK;
}
/* receive messages */
/*******************************************************************************
 * @brief      Get an CAN frame from Rx mail box.
 * @param[in]  CANDriver *cand
 *             uint32_t msg_box
 * @param[out] CANRxMsg *rx_msg
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_Recv_Msg_From_MB(const CANDriver *cand, uint32_t msg_box, CANRxMsg *rx_msg)
{
    volatile CANReg *reg = NULL;
    uint32_t mb_idx;
    uint32_t rx_flag;

    /* fetch register */
    reg = Can_LLD_Get_Reg(cand->id);

    /* check mailbox */
    mb_idx = Can_LLD_Get_Rx_MB_Idx_Offset(cand->id) + msg_box;
    if (mb_idx < CAN_WORD_INDICATE_MAIL_BOX_NUM)
    {
        rx_flag = BIT_GET(reg->IFRL.R, mb_idx); /* PRQA S 3469 # simple macro function */
    }
    else
    {
        rx_flag = BIT_GET(reg->IFRH.R, mb_idx - CAN_WORD_INDICATE_MAIL_BOX_NUM); /* PRQA S 3469 # simple macro function */
    }

    if (rx_flag == 0U)
    {
        return CAN_ERROR_MAIL_BOX_EMPTY;
    }
    if ((reg->BUF[mb_idx].CS.B.CODE != CAN_RX_MB_STATE_RECVED_MSG) &&
        (reg->BUF[mb_idx].CS.B.CODE != CAN_RX_MB_STATE_OVERWRITTEN))
    {
        return CAN_ERROR_MAIL_BOX_STATE;
    }
    /* read message */
    Can_LLD_Get_Buf_In_MB(reg, mb_idx, rx_msg);
    /* release message */ /* Write-clear */
    if (mb_idx < CAN_WORD_INDICATE_MAIL_BOX_NUM)
    {
        reg->IFRL.R = 1UL << mb_idx;
    }
    else
    {
        reg->IFRH.R = 1UL << (mb_idx - CAN_WORD_INDICATE_MAIL_BOX_NUM);
    }

    return CAN_OK;
}

/*******************************************************************************
 * @brief      Get an CAN frame from Rx FIFO.
 * @param[in]  CANDriver *cand
 * @param[out] CANRxMsg *rx_msg
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_Recv_Msg_From_FIFO(const CANDriver *cand, CANRxMsg *rx_msg)
{
    volatile CANReg *reg = NULL;
    volatile uint32_t rx_flag;

    /* fetch register */
    reg = Can_LLD_Get_Reg(cand->id);

    /* check FIFO */
    rx_flag = reg->IFRL.B.BUF05I; /* BUF5I indicate Frames available in FIFO */
    if (rx_flag == 0U)
    {
        return CAN_ERROR_FIFO_EMPTY;
    }
    /* read message */
    Can_LLD_Get_Buf_In_FIFO(reg, rx_msg);

    /* release message */ /* Write-clear */
    reg->IFRL.R = (1UL << 5UL);

    return CAN_OK;
}

/* low power */
/*******************************************************************************
 * @brief      enter to sleep mode.
 * @param[in]  CANDriver *cand
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Can_LLD_Sleep(CANDriver * cand)
{
    volatile CANReg *reg = NULL;

    /* fetch register */
    reg = Can_LLD_Get_Reg(cand->id);
    /* global disable */
    reg->MCR.B.MDIS = ENABLE; /* set MDIS 1 */
    /* Wait feedback */
    while (reg->MCR.B.LPMACK == DISABLE)
    {
        ;
    }
    /* update status */
    cand->state.cur_mode = CAN_CUR_MODE_SLEEP;
}

/*******************************************************************************
 * @brief       Wake up from sleep mode.
 * @param[in]  CANDriver *cand
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Can_LLD_Wakeup(CANDriver * cand)
{
    volatile CANReg *reg = NULL;

    /* fetch register */
    reg = Can_LLD_Get_Reg(cand->id);
    /* global enable */
    reg->MCR.B.MDIS = DISABLE; /* set MDIS 0 */
    /* update status */
    cand->state.cur_mode = CAN_CUR_MODE_NORMAL;
}

/* CAN init */
/*******************************************************************************
 * @brief      Initializes the CAN peripheral bnase on the specified parameters.
 * @param[in]  const CANConfig *config
 * @param[out] CANDriver **cand
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_Init(CANDriver **cand, const CANConfig *config)
{
    uint32_t ret;
    volatile CANReg *reg = NULL;

    /* check configuration */
    ret = Can_LLD_Cfg_Check(config);
    if (ret != CAN_OK)
    {
        return ret;
    }
    /* mount descriptor */
    *cand = Can_LLD_Get_Driver(config->can_id);
    (*cand)->id = config->can_id;
    /* check status */
    if ((*cand)->state.cur_mode != CAN_CUR_MODE_INIT)
    {
        return CAN_ERROR_INVALID_STATE;
    }
    /* mapped port */
    Can_LLD_Init_Port((*cand)->id);
    /* fetch register */
    reg = Can_LLD_Get_Reg((*cand)->id);
    /* enable clock */
    Can_LLD_Config_Clock((*cand)->id, ENABLE);
    reg->CR.B.CLKSRC = config->phy_para.use_bus_clk_src;
    /* global enable */
    reg->MCR.B.MDIS = DISABLE; /* set MDIS 0 */
    /* config bit rate */
    Can_LLD_Set_Rate(reg, config);
    /* alloc mailbox */
    reg->MCR.B.MAXMB = CAN_TOTAL_MAIL_BOX_NUM - 1U;
    /* init TX MB */
    Can_LLD_Init_Tx_MB(reg, config);
    /* init RX MB */
    Can_LLD_Init_Rx_MB(reg, config);
    /* init RX FIFO */
    Can_LLD_Init_Rx_FIFO(reg, config);
    /* enable interrupt */
    Can_LLD_Init_Intr(reg, config);
    /* select mode */
    Can_LLD_Select_Run_Mode(*cand, reg, config);
    /* Clear error code */
    reg->ECR.R = 0;
    reg->ESR.R = 0;
    /* start running */
    reg->MCR.B.HALT = DISABLE;
    reg->MCR.B.FRZ = DISABLE;

    return CAN_OK;
}

/*******************************************************************************
 * @brief      Deinitializes the CAN peripheral.
 * @param[in]  CANDriver *cand
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Can_LLD_DeInit(CANDriver *cand)
{
    volatile CANReg *reg = NULL;
    uint8_t i;
    if (cand->state.cur_mode == CAN_CUR_MODE_INIT)
    {
        return CAN_ERROR_INVALID_STATE;
    }
    /* fetch register */
    reg = Can_LLD_Get_Reg(cand->id);
    /* configure soft reset */
    reg->MCR.B.SOFTRST = ENABLE;
    // while (reg->MCR.B.SOFTRST == ENABLE)
    // {
    //     ;
    // }
    for ( i = 0; i < 10000; i++)
    {
        if (reg->MCR.B.SOFTRST != ENABLE)
        {
            break;
        }
        
    }
    
    /* sisable self-loopback */
    reg->CR.B.LPB = DISABLE;
    /* globally disable */
    reg->MCR.B.MDIS = ENABLE; /* set MDIS 1 */
    /* disable clock */
    Can_LLD_Config_Clock(cand->id, DISABLE);
    /* clean handle */
    cc_memset((uint8_t *)cand, 0, sizeof(CANDriver));
    /* update status */
    cand->state.cur_mode = CAN_CUR_MODE_INIT;

    return CAN_OK;
}

